package beautiful.butterfly.drds.data_exchange.util;

import java.math.BigInteger;
import java.util.*;

/**
 * 提供通用的根据数字范围、字符串范围等进行切分的通用功能.
 */
public final class RangeSplits
{

    public static String[] doAsciiStringSplit(String leftValue, String rightValue, int expectSliceNumber)
    {
        int radix = 128;

        BigInteger[] bigIntegers = doBigIntegerSplit(stringToBigInteger(leftValue, radix), stringToBigInteger(rightValue, radix), expectSliceNumber);
        String[] strings = new String[bigIntegers.length];

        //处理第一个字符串（因为：在转换为数字，再还原的时候，如果首字符刚好是 basic,则不知道应该添加多少个 basic）
        strings[0] = leftValue;
        strings[bigIntegers.length - 1] = rightValue;
        for (int i = 1, length = bigIntegers.length - 1; i < length; i++)
        {
            strings[i] = bigIntegerToString(bigIntegers[i], radix);
        }

        return strings;
    }


    public static long[] doLongSplit(long leftValue, long rightValue, int expectSliceNumber)
    {
        BigInteger[] bigIntegers = doBigIntegerSplit(BigInteger.valueOf(leftValue), BigInteger.valueOf(rightValue), expectSliceNumber);
        long[] longs = new long[bigIntegers.length];
        for (int i = 0, len = bigIntegers.length; i < len; i++)
        {
            longs[i] = bigIntegers[i].longValue();
        }
        return longs;
    }

    public static BigInteger[] doBigIntegerSplit(BigInteger leftValue, BigInteger rightValue, int expectSliceNumber)
    {
        if (expectSliceNumber < 1)
        {
            throw new IllegalArgumentException(String.format(
                    "切分份数不能小于1. 此处:expectSliceNumber=[%s].", expectSliceNumber));
        }

        if (null == leftValue || null == rightValue)
        {
            throw new IllegalArgumentException(String.format(
                    "对 BigInteger 进行切分时，其左右区间不能为 null. 此处:left=[%s],right=[%s].", leftValue, rightValue));
        }

        if (leftValue.compareTo(rightValue) == 0)
        {
            return new BigInteger[]{leftValue, rightValue};
        } else
        {
            // 调整大小顺序，确保 left < right
            if (leftValue.compareTo(rightValue) > 0)
            {
                BigInteger $ = leftValue;
                leftValue = rightValue;
                rightValue = $;
            }

            //left < right
            BigInteger subtractNum = rightValue.subtract(leftValue);

            BigInteger step = subtractNum.divide(BigInteger.valueOf(expectSliceNumber));
            BigInteger remainder = subtractNum.remainder(BigInteger.valueOf(expectSliceNumber));

            //remainder 不可能超过expectSliceNumber,所以不需要检查remainder的 Integer 的范围

            // 这里不能 step.intValue()==0，因为可能溢出
            if (step.compareTo(BigInteger.ZERO) == 0)
            {
                expectSliceNumber = remainder.intValue();
            }

            BigInteger[] bigIntegers = new BigInteger[expectSliceNumber + 1];
            bigIntegers[0] = leftValue;
            bigIntegers[expectSliceNumber] = rightValue;

            BigInteger lowerBound;
            BigInteger upperBound = leftValue;
            for (int i = 1; i < expectSliceNumber; i++)
            {
                lowerBound = upperBound;
                upperBound = lowerBound.add(step);
                upperBound = upperBound.add((remainder.compareTo(BigInteger.valueOf(i)) >= 0) ? BigInteger.ONE : BigInteger.ZERO);
                bigIntegers[i] = upperBound;
            }

            return bigIntegers;
        }
    }

    private static void checkIsBetweenRange(int value, int leftValue, int rightValue)
    {
        if (value < leftValue || value > rightValue)
        {
            throw new IllegalArgumentException(String.format("parameter can not <[%s] or >[%s].",
                    leftValue, rightValue));
        }
    }


    /**
     * 把BigInteger 转换为 String.注意：radix 和 basic 范围都为[1,128], radix + basic 的范围也必须在[1,128].
     */
    private static String bigIntegerToString(BigInteger bigInteger, int radix)
    {
        if (null == bigInteger)
        {
            throw new IllegalArgumentException("参数 bigInteger 不能为空.");
        }

        checkIsBetweenRange(radix, 1, 128);
//
        Map<Integer, Character> map = new HashMap<Integer, Character>();
        for (int i = 0; i < radix; i++)
        {
            map.put(i, (char) (i));
        }
        //
        StringBuilder sb = new StringBuilder();

        List<Integer> list = new ArrayList<Integer>();

        BigInteger bigIntegerCopy = bigInteger;
        BigInteger radixBigInteger = BigInteger.valueOf(radix);
        //
        BigInteger divideLeftBigInteger = bigIntegerCopy.divide(radixBigInteger);
        while (divideLeftBigInteger.compareTo(BigInteger.ZERO) > 0)
        {
            list.add(bigIntegerCopy.remainder(radixBigInteger).intValue());
            bigIntegerCopy = bigIntegerCopy.divide(radixBigInteger);
            divideLeftBigInteger = bigIntegerCopy;
        }
        Collections.reverse(list);
        //
        if (list.isEmpty())
        {
            list.add(0, bigInteger.remainder(radixBigInteger).intValue());
        }


        for (Integer integer : list)
        {
            sb.append(map.get(integer));
        }

        return sb.toString();
    }

    /**
     * 由于只支持 ascii 码对应字符，所以radix 范围为[1,128]
     */
    public static BigInteger stringToBigInteger(String string, int radix)
    {
        if (null == string)
        {
            throw new IllegalArgumentException("参数 bigInteger 不能为空.");
        }

        checkIsBetweenRange(radix, 1, 128);

        BigInteger bigInteger = BigInteger.ZERO;
        BigInteger radixBigInteger = BigInteger.valueOf(radix);

        int aChar;
        int pow = 0;

        for (int i = string.length() - 1; i >= 0; i--)
        {
            aChar = string.charAt(i);
            if (aChar >= 128)
            {
                throw new IllegalArgumentException(String.format("根据字符串进行切分时仅支持 ASCII 字符串，而字符串:[%s]非 ASCII 字符串.", string));
            }
            bigInteger = bigInteger.add(BigInteger.valueOf(aChar).multiply(radixBigInteger.pow(pow)));
            pow++;
        }

        return bigInteger;
    }


}
